Skip to content

Commit 4209441

Browse files
committed
basics of JS i18n
1 parent d043a2b commit 4209441

13 files changed

+2653
-106
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ Refer to https://indigo.readthedocs.io/en/latest/running/index.html
2121

2222
## Adding translation strings
2323

24-
Each indigo package has its own translations in the `locale` directory. Translations for strings are added on [CrowdIn](https://crowdin.com/project/lawsafrica-indigo).
24+
Each indigo package has its own translations in the `locale` directory, and Javascript translations are in
25+
`static/i18n`. Translations for strings are added on [CrowdIn](https://crowdin.com/project/lawsafrica-indigo).
2526

2627
If you have added or changed strings that need translating, you must [tell Django to update the .po files](https://docs.djangoproject.com/en/2.2/topics/i18n/translation/#localization-how-to-create-language-files) so that translations can be supplied through CrowdIn.
2728

2829
```bash
29-
for app in indigo indigo_api indigo_za; do pushd $app; django-admin makemessages -a; popd; done
30+
scripts/extract-translations.sh
31+
npm run extract-translations
3032
```
3133

3234
And then commit the changes. CrowdIn will pick up any changed strings and make them available for translation. Once they are translated, it will

crowdin.yml

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ files:
99
translation: /indigo_social/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%
1010
- source: /indigo_za/locale/en/LC_MESSAGES/django.po
1111
translation: /indigo_za/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%
12+
- source: /indigo_app/static/i18n/indigo_app-en.json
13+
translation: /indigo_app/static/i18n/indigo_app-%two_letters_code%.json

i18next-parser.config.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
"defaultNamespace": "indigo_app",
3+
"locales": [
4+
"en",
5+
"fr"
6+
],
7+
"keySeparator": false,
8+
"lexers": {
9+
"js": [
10+
{
11+
"lexer": "JavascriptLexer",
12+
"functions": ["t", "$t"]
13+
}
14+
]
15+
},
16+
"input": [
17+
"indigo_app/static/javascript/indigo/**/*.js",
18+
],
19+
"output": "indigo_app/static/i18n/$NAMESPACE-$LOCALE.json"
20+
}

indigo/settings.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@
161161
'v2': 'indigo_content_api.v2.urls_api',
162162
'v3': 'indigo_content_api.v3.urls_api',
163163
}
164-
}
164+
},
165+
166+
# namespaces to look for translation packs for javascript translation via i18next
167+
'JS_I18N_NAMESPACES': ['indigo_app'],
165168
}
166169

167170
# Database

indigo_app/context_processors.py

+23
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import json
44

5+
from django.utils.translation import get_language
6+
from django.templatetags.static import static
7+
58

69
def general(request):
710
"""
@@ -16,6 +19,7 @@ def general(request):
1619
'USER_JSON': serialise_user(request),
1720
'MAINTENANCE_MODE': settings.INDIGO['MAINTENANCE_MODE'],
1821
'SENTRY_DSN': settings.SENTRY_DSN,
22+
'JS_I18N': json.dumps(js_i18n(request)),
1923
}
2024

2125

@@ -41,3 +45,22 @@ def serialise_user(request):
4145
data = UserDetailsSerializer(context={'request': request}).to_representation(request.user)
4246

4347
return json.dumps(data)
48+
49+
50+
def js_i18n(request):
51+
""" Configuration for i18next for javascript translations."""
52+
paths = {
53+
f'{ns}-{code}': static(f'i18n/{ns}-{code}.json')
54+
for code, name in settings.LANGUAGES
55+
for ns in settings.INDIGO['JS_I18N_NAMESPACES']
56+
}
57+
58+
return {
59+
'loadPaths': paths,
60+
'ns': 'indigo_app',
61+
'lng': get_language(),
62+
'fallbackLng': 'en',
63+
'returnEmptyString': False,
64+
'keySeparator': False,
65+
'debug': settings.DEBUG,
66+
}

indigo_app/js/indigo.js

+14
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import './compat-imports';
1414
import { relativeTimestamps } from './timestamps';
1515
import htmx from 'htmx.org';
1616
import { createComponent, getVue, registerComponents } from './vue';
17+
import i18next from 'i18next';
18+
import HttpApi from 'i18next-http-backend';
1719

1820
customElements.define('la-akoma-ntoso', LaAkomaNtoso);
1921
customElements.define('la-gutter', LaGutter);
@@ -30,6 +32,7 @@ class IndigoApp {
3032
this.components = [];
3133
this.componentLibrary = {};
3234
this.Vue = getVue();
35+
this.setupI18n();
3336
this.setupHtmx();
3437

3538
for (const [name, component] of Object.entries(components)) {
@@ -45,6 +48,17 @@ class IndigoApp {
4548
window.dispatchEvent(new Event('indigo.components-created'));
4649
}
4750

51+
setupI18n () {
52+
const opts = window.Indigo.i18n;
53+
opts.backend = {};
54+
opts.backend.loadPath = function (languages, namespaces) {
55+
return opts.loadPaths[namespaces[0] + '-' + languages[0]];
56+
};
57+
i18next.use(HttpApi).init(opts);
58+
// setup a global translation function
59+
window.$t = i18next.t.bind(i18next);
60+
}
61+
4862
setupHtmx () {
4963
window.htmx = htmx;
5064
document.body.addEventListener('htmx:configRequest', (e) => {
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"Save": "",
3+
"Cancel": "",
4+
"Are you sure you want to resolve this?": "",
5+
"Reply": "",
6+
"Publish this document to users?": "",
7+
"Hide this document from users?": "",
8+
"You cannot delete published documents. Please mark the document as a draft and try again.": "",
9+
"Are you sure you want to delete this document?": ""
10+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"Save": "Sauvegarder",
3+
"Cancel": "Annuler",
4+
"Are you sure you want to resolve this?": "",
5+
"Reply": "",
6+
"Publish this document to users?": "",
7+
"Hide this document from users?": "",
8+
"You cannot delete published documents. Please mark the document as a draft and try again.": "",
9+
"Are you sure you want to delete this document?": ""
10+
}

indigo_app/static/javascript/indigo/views/annotations.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@
100100

101101
this.$el
102102
.find('.button-container')
103-
.append('<button class="btn btn-primary btn-sm save">Save</button>')
104-
.append('<button class="btn btn-outline-secondary btn-sm unedit float-end">Cancel</button>')
103+
.append('<button class="btn btn-primary btn-sm save">' + $t('Save') + '</button>')
104+
.append('<button class="btn btn-outline-secondary btn-sm unedit float-end">' + $t('Cancel') + '</button>')
105105
.end()
106106
.find('.content')
107107
.replaceWith($textarea);
@@ -115,7 +115,7 @@
115115
},
116116

117117
close: function(e) {
118-
if (confirm('Are you sure you want to resolve this?')) {
118+
if (confirm($t('Are you sure you want to resolve this?'))) {
119119
this.model.set('closed', true);
120120
this.model.save();
121121
}
@@ -228,8 +228,8 @@
228228

229229
if (Indigo.user.hasPerm('indigo_api.add_annotation')) {
230230
$('<div class="annotation reply-container">')
231-
.append('<textarea class="form-control reply-box" placeholder="Reply...">')
232-
.append('<button class="btn btn-primary btn-sm post hidden" disabled>Reply</button>')
231+
.append('<textarea class="form-control reply-box" placeholder="' + $t('Reply') + '...">')
232+
.append('<button class="btn btn-primary btn-sm post hidden" disabled>' + $t('Reply') + '</button>')
233233
.appendTo(this.el);
234234
}
235235
},

indigo_app/static/javascript/indigo/views/document.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,14 @@
260260
},
261261

262262
saveAndPublish: function() {
263-
if (Indigo.user.hasPerm('indigo_api.publish_document') && confirm('Publish this document to users?')) {
263+
if (Indigo.user.hasPerm('indigo_api.publish_document') && confirm($t('Publish this document to users?'))) {
264264
this.document.set('draft', false);
265265
this.save();
266266
}
267267
},
268268

269269
saveAndUnpublish: function() {
270-
if (Indigo.user.hasPerm('indigo_api.publish_document') && confirm('Hide this document from users?')) {
270+
if (Indigo.user.hasPerm('indigo_api.publish_document') && confirm($t('Hide this document from users?'))) {
271271
this.document.set('draft', true);
272272
this.save();
273273
}
@@ -313,11 +313,11 @@
313313

314314
delete: function() {
315315
if (!this.document.get('draft')) {
316-
alert('You cannot delete published documents. Please mark the document as a draft and try again.');
316+
alert($t('You cannot delete published documents. Please mark the document as a draft and try again.'));
317317
return;
318318
}
319319

320-
if (confirm('Are you sure you want to delete this document?')) {
320+
if (confirm($t('Are you sure you want to delete this document?'))) {
321321
var frbr_uri = this.document.work.get('frbr_uri');
322322

323323
Indigo.progressView.peg();

indigo_app/templates/base.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% load pipeline sass_tags static account django_htmx %}
1+
{% load pipeline sass_tags static account django_htmx i18n %}
22

33
<!DOCTYPE html>
44
<html>
@@ -81,6 +81,7 @@
8181
if (!window.Indigo) window.Indigo = {};
8282
if (!window.Indigo.Preloads) window.Indigo.Preloads = {};
8383

84+
window.Indigo.i18n = {{ JS_I18N|safe }};
8485
window.Indigo.resolverUrl = '{{ RESOLVER_URL }}';
8586
{% if user.is_authenticated %}
8687
window.Indigo.Preloads.user = {{ USER_JSON|safe }};

0 commit comments

Comments
 (0)