mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Bump i18n-js to v4
This commit is contained in:
+1
-1
@@ -64,7 +64,7 @@ npm-debug.log*
|
||||
# Generated files
|
||||
/app/assets/javascripts/editor/*
|
||||
/app/assets/javascripts/locales/*.*
|
||||
/frontend/src/locales/*.js
|
||||
/frontend/src/locales/*
|
||||
/modules/*/frontend/module/module
|
||||
/config/additional_environment.rb
|
||||
/config/configuration.yml
|
||||
|
||||
@@ -170,7 +170,7 @@ group :production do
|
||||
gem 'dalli', '~> 3.2.0'
|
||||
end
|
||||
|
||||
gem 'i18n-js', '~> 3.9.0'
|
||||
gem 'i18n-js', '~> 4.2.3'
|
||||
gem 'rails-i18n', '~> 7.0.0'
|
||||
|
||||
gem 'sprockets', '~> 3.7.2' # lock sprockets below 4.0
|
||||
|
||||
+5
-3
@@ -480,6 +480,7 @@ GEM
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
glob (0.4.0)
|
||||
globalid (1.1.0)
|
||||
activesupport (>= 5.0)
|
||||
gon (6.4.0)
|
||||
@@ -530,8 +531,9 @@ GEM
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.9.2)
|
||||
i18n (>= 0.6.6)
|
||||
i18n-js (4.2.3)
|
||||
glob (>= 0.4.0)
|
||||
i18n
|
||||
icalendar (2.8.0)
|
||||
ice_cube (~> 0.16)
|
||||
ice_cube (0.16.4)
|
||||
@@ -1034,7 +1036,7 @@ DEPENDENCIES
|
||||
grids!
|
||||
html-pipeline (~> 2.14.0)
|
||||
htmldiff
|
||||
i18n-js (~> 3.9.0)
|
||||
i18n-js (~> 4.2.3)
|
||||
json_schemer (~> 1.0.1)
|
||||
json_spec (~> 1.1.4)
|
||||
ladle
|
||||
|
||||
@@ -34,9 +34,6 @@ OpenProject::Application.configure do
|
||||
# since you don't have to restart the web server when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
# Automatically refresh translations with I18n middleware
|
||||
config.middleware.use ::I18n::JS::Middleware
|
||||
|
||||
# Do not eager load code on boot by default.
|
||||
config.eager_load = ENV['EAGER_LOAD'].present?
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
fallbacks: :en
|
||||
translations:
|
||||
- file: "frontend/src/locales/%{locale}.js"
|
||||
only: ['*.js', '*.number.*', '*.time.*', '*.date.*']
|
||||
@@ -0,0 +1,11 @@
|
||||
# This file controls ONLY the translation
|
||||
# for the frontend (i18n-js)
|
||||
embed_fallback_translations:
|
||||
enabled: true
|
||||
translations:
|
||||
- file: "frontend/src/locales/:locale.json"
|
||||
patterns:
|
||||
- '*.js'
|
||||
- '*.number.*'
|
||||
- '*.time.*'
|
||||
- '*.date.*'
|
||||
@@ -0,0 +1,7 @@
|
||||
# Auto-build js translations in dev mode
|
||||
Rails.application.config.after_initialize do
|
||||
if Rails.env.development?
|
||||
require "i18n-js/listen"
|
||||
I18nJS.listen
|
||||
end
|
||||
end
|
||||
Generated
+42
@@ -65,6 +65,7 @@
|
||||
"fuse.js": "^3.4.5",
|
||||
"glob": "^7.1.4",
|
||||
"hammerjs": "^2.0.8",
|
||||
"i18n-js": "^4.3.0",
|
||||
"jquery": "^3.5.1",
|
||||
"jquery-ui": "1.13.2",
|
||||
"jquery-ujs": "^1.2.2",
|
||||
@@ -19969,6 +19970,14 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
|
||||
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"license": "MIT",
|
||||
@@ -26876,6 +26885,15 @@
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18n-js": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-4.3.0.tgz",
|
||||
"integrity": "sha512-PX93eT6WPV6Ym6mHtFKGDRZB0zwDX7HUPkgprjsZ28J6/Ohw1nvRYuM93or3pWv2VLxs6XfBf7X9Fc/YAZNEtQ==",
|
||||
"dependencies": {
|
||||
"bignumber.js": "*",
|
||||
"make-plural": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "22.5.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-22.5.1.tgz",
|
||||
@@ -29496,6 +29514,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/make-plural": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.3.0.tgz",
|
||||
"integrity": "sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw=="
|
||||
},
|
||||
"node_modules/makeerror": {
|
||||
"version": "1.0.12",
|
||||
"dev": true,
|
||||
@@ -52691,6 +52714,11 @@
|
||||
"version": "5.2.2",
|
||||
"dev": true
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
|
||||
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
@@ -57417,6 +57445,15 @@
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"i18n-js": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-4.3.0.tgz",
|
||||
"integrity": "sha512-PX93eT6WPV6Ym6mHtFKGDRZB0zwDX7HUPkgprjsZ28J6/Ohw1nvRYuM93or3pWv2VLxs6XfBf7X9Fc/YAZNEtQ==",
|
||||
"requires": {
|
||||
"bignumber.js": "*",
|
||||
"make-plural": "*"
|
||||
}
|
||||
},
|
||||
"i18next": {
|
||||
"version": "22.5.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-22.5.1.tgz",
|
||||
@@ -59130,6 +59167,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-plural": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.3.0.tgz",
|
||||
"integrity": "sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw=="
|
||||
},
|
||||
"makeerror": {
|
||||
"version": "1.0.12",
|
||||
"dev": true,
|
||||
|
||||
@@ -137,6 +137,7 @@
|
||||
"fuse.js": "^3.4.5",
|
||||
"glob": "^7.1.4",
|
||||
"hammerjs": "^2.0.8",
|
||||
"i18n-js": "^4.3.0",
|
||||
"jquery": "^3.5.1",
|
||||
"jquery-ui": "1.13.2",
|
||||
"jquery-ujs": "^1.2.2",
|
||||
|
||||
@@ -1,70 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
/**
|
||||
* General components
|
||||
*/
|
||||
export interface GlobalI18n {
|
||||
t<T=string>(translateId:string, parameters?:unknown):T;
|
||||
|
||||
lookup(translateId:string):boolean|undefined;
|
||||
|
||||
toNumber(num:number, options?:ToNumberOptions):string;
|
||||
|
||||
toPercentage(num:number, options?:ToPercentageOptions):string;
|
||||
|
||||
toCurrency(num:number, options?:ToCurrencyOptions):string;
|
||||
|
||||
strftime(date:Date, format:string):string;
|
||||
|
||||
toHumanSize(num:number, options?:ToHumanSizeOptions):string;
|
||||
|
||||
toTime(format:string, date:Date):string;
|
||||
|
||||
locale:string;
|
||||
firstDayOfWeek:number;
|
||||
pluralization:any;
|
||||
}
|
||||
|
||||
interface ToNumberOptions {
|
||||
precision?:number;
|
||||
separator?:string;
|
||||
delimiter?:string;
|
||||
strip_insignificant_zeros?:boolean;
|
||||
}
|
||||
|
||||
type ToPercentageOptions = ToNumberOptions;
|
||||
|
||||
interface ToCurrencyOptions extends ToNumberOptions {
|
||||
format?:string;
|
||||
unit?:string;
|
||||
sign_first?:boolean;
|
||||
}
|
||||
|
||||
interface ToHumanSizeOptions extends ToNumberOptions {
|
||||
format?:string;
|
||||
}
|
||||
import { I18n } from 'i18n-js';
|
||||
import { FormatNumberOptions, TranslateOptions } from 'i18n-js/src/typing';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class I18nService {
|
||||
private i18n:GlobalI18n = window.I18n;
|
||||
private i18n:I18n = window.I18n;
|
||||
|
||||
public get locale():string {
|
||||
return this.i18n.locale;
|
||||
}
|
||||
|
||||
public t = this.i18n.t.bind(this.i18n) as GlobalI18n['t'];
|
||||
public t<T = string>(input:string, options:Partial<TranslateOptions> = {}) {
|
||||
return this.i18n.t<T>(input, options);
|
||||
}
|
||||
|
||||
public lookup = this.i18n.lookup.bind(this.i18n) as GlobalI18n['lookup'];
|
||||
public toTime = this.i18n.toTime.bind(this.i18n);
|
||||
|
||||
public toTime = this.i18n.toTime.bind(this.i18n) as GlobalI18n['toTime'];
|
||||
|
||||
public toNumber = this.i18n.toNumber.bind(this.i18n) as GlobalI18n['toNumber'];
|
||||
|
||||
public toPercentage = this.i18n.toPercentage.bind(this.i18n) as GlobalI18n['toPercentage'];
|
||||
|
||||
public toCurrency = this.i18n.toCurrency.bind(this.i18n) as GlobalI18n['toCurrency'];
|
||||
|
||||
public strftime = this.i18n.strftime.bind(this.i18n) as GlobalI18n['strftime'];
|
||||
|
||||
public toHumanSize = this.i18n.toHumanSize.bind(this.i18n) as GlobalI18n['toHumanSize'];
|
||||
public toNumber(val:string|number, options:Partial<FormatNumberOptions>):string {
|
||||
return this.i18n.localize('number', val, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
//++
|
||||
|
||||
import * as moment from 'moment';
|
||||
import * as i18njs from 'i18n-js';
|
||||
|
||||
export function initializeLocale() {
|
||||
const meta = document.querySelector('meta[name=openproject_initializer]') as HTMLMetaElement;
|
||||
@@ -34,10 +35,10 @@ export function initializeLocale() {
|
||||
const firstDayOfWeek = parseInt(meta.dataset.firstdayofweek || '', 10); // properties of meta.dataset are exposed in lowercase
|
||||
const firstWeekOfYear = parseInt(meta.dataset.firstweekofyear || '', 10); // properties of meta.dataset are exposed in lowercase
|
||||
|
||||
window.I18n = new i18njs.I18n();
|
||||
I18n.locale = locale;
|
||||
|
||||
if (!Number.isNaN(firstDayOfWeek) && !Number.isNaN(firstWeekOfYear)) {
|
||||
I18n.firstDayOfWeek = firstDayOfWeek;
|
||||
moment.updateLocale(locale, {
|
||||
week: {
|
||||
dow: firstDayOfWeek,
|
||||
@@ -49,16 +50,22 @@ export function initializeLocale() {
|
||||
// Override the default pluralization function to allow
|
||||
// "other" to be used as a fallback for "one" in languages where one is not set
|
||||
// (japanese, for example)
|
||||
I18n.pluralization.default = function (count:number) {
|
||||
switch (count) {
|
||||
case 0:
|
||||
return ['zero', 'other'];
|
||||
case 1:
|
||||
return ['one', 'other'];
|
||||
default:
|
||||
return ['other'];
|
||||
}
|
||||
};
|
||||
I18n.pluralization.register(
|
||||
'default',
|
||||
(_i18n:i18njs.I18n, count:number) => {
|
||||
switch (count) {
|
||||
case 0:
|
||||
return ['zero', 'other'];
|
||||
case 1:
|
||||
return ['one', 'other'];
|
||||
default:
|
||||
return ['other'];
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return import(/* webpackChunkName: "locale" */ `../../../locales/${I18n.locale}.js`);
|
||||
return import(/* webpackChunkName: "locale" */ `../../../locales/${I18n.locale}.json`)
|
||||
.then((imported:{ default:object }) => {
|
||||
I18n.store(imported.default);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,7 +66,5 @@ require('moment-timezone/builds/moment-timezone-with-data.min.js');
|
||||
require('expose-loader?URI!urijs');
|
||||
require('urijs/src/URITemplate');
|
||||
|
||||
require('expose-loader?I18n!core-vendor/i18n');
|
||||
|
||||
// Localization for fullcalendar
|
||||
require('@fullcalendar/core/locales-all');
|
||||
|
||||
@@ -369,7 +369,7 @@ export class TimeEntryCalendarComponent {
|
||||
rendering: 'background' as const,
|
||||
startEditable: false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
sum: this.i18n.t('js.units.hour', { count: this.formatNumber(duration) }),
|
||||
sum: this.i18n.t('js.units.hour', { count: duration }),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export class HalResourceNotificationService {
|
||||
const attributeType = schema.type.toLowerCase();
|
||||
const i18nString = `js.hal.error.format.${attributeType}`;
|
||||
|
||||
if (this.I18n.lookup(i18nString) === undefined) {
|
||||
if (this.I18n.t(i18nString, { default: '[not found]' }) === '[not found]') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ export class OpAttachmentListItemComponent extends UntilDestroyedMixin implement
|
||||
dragHint: this.I18n.t('js.attachments.draggable_hint'),
|
||||
deleteTitle: this.I18n.t('js.attachments.delete'),
|
||||
deleteConfirmation: this.I18n.t('js.attachments.delete_confirmation'),
|
||||
removeFile: (arg:unknown):string => this.I18n.t('js.label_remove_file', arg),
|
||||
removeFile: (arg:object):string => this.I18n.t('js.label_remove_file', arg),
|
||||
};
|
||||
|
||||
public get deleteIconTitle():string {
|
||||
|
||||
Vendored
+3
-2
@@ -22,6 +22,7 @@ import { GlobalI18n } from 'core-app/core/i18n/i18n.service';
|
||||
import { Dragula } from 'dragula';
|
||||
import { Screenfull } from 'screenfull';
|
||||
import { ErrorReporterBase } from 'core-app/core/errors/error-reporter-base';
|
||||
import { I18n } from 'i18n-js';
|
||||
|
||||
declare module 'observable-array';
|
||||
declare module 'dom-autoscroller';
|
||||
@@ -29,13 +30,13 @@ declare module 'core-vendor/enjoyhint';
|
||||
|
||||
declare global {
|
||||
const _:typeof TLodash;
|
||||
const I18n:GlobalI18n;
|
||||
const I18n:I18n;
|
||||
const dragula:Dragula;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
I18n:GlobalI18n;
|
||||
I18n:I18n;
|
||||
appBasePath:string;
|
||||
ng2Injector:Injector;
|
||||
OpenProject:OpenProject;
|
||||
|
||||
Vendored
-1067
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strictNullChecks": true,
|
||||
"strictBindCallApply": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./src/",
|
||||
"typeRoots": [
|
||||
|
||||
@@ -78,5 +78,9 @@ namespace :assets do
|
||||
end
|
||||
|
||||
desc 'Export frontend locale files'
|
||||
task export_locales: ['i18n:js:export']
|
||||
task :export_locales do
|
||||
sh('bundle exec i18n export') do |ok, res|
|
||||
raise "Failed to export i18n-js translations: #{res.exitstatus}" if !ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user