Commit e0226f93 authored by Steffen van Bergerem's avatar Steffen van Bergerem

Raise on missing translations

parent 715cb1bb
......@@ -285,7 +285,7 @@ app.views.Publisher = Backbone.View.extend({
var pollAnswersArray = _.flatten([serializedForm["poll_answers[]"]]);
var pollAnswers = _.map(pollAnswersArray, function(answer){
if (answer) {
return { "answer" : answer };
return {"answer": answer, "vote_count": 0};
}
});
pollAnswers = _.without(pollAnswers, undefined);
......
......@@ -20,12 +20,11 @@ Diaspora.I18n = {
updateLocale: function(locale, data) {
locale.data = $.extend(locale.data, data);
var rule = this._resolve(locale, ['pluralization_rule']);
if (rule !== "") {
/* jshint evil:true */
// TODO change this to `locale.pluralizationKey = rule`?
var rule = locale.data.pluralization_rule;
if (typeof rule !== "undefined") {
/* eslint-disable no-eval */
eval("locale.pluralizationKey = "+rule);
/* jshint evil:false */
/* eslint-enable no-eval */
}
},
......@@ -46,14 +45,9 @@ Diaspora.I18n = {
: locale.data[nextNamespace];
if(typeof translatedMessage === "undefined") {
if (typeof locale.fallback === "undefined") {
return "";
} else {
return this._resolve(locale.fallback, originalItems);
}
throw new Error("Missing translation: " + originalItems.join("."));
}
}
return translatedMessage;
},
......@@ -68,7 +62,7 @@ Diaspora.I18n = {
return _.template(this._resolve(locale, items))(views || {});
} catch (e) {
if (typeof locale.fallback === "undefined") {
return "";
throw e;
} else {
return this._render(locale.fallback, originalItems, views);
}
......@@ -86,4 +80,3 @@ Diaspora.I18n = {
}
};
// @license-end
module LanguageHelper
include ApplicationHelper
def available_language_options
options = []
AVAILABLE_LANGUAGES.each do |locale, language|
......
%div
%select.aspect_dropdown.form-control.user_aspects{"name" => "user_aspects", "data-person-id" => @person.id}
%option{value: 'list_cover', class: 'list_cover', disabled: 'true', selected: 'true'}
= t("add_contact")
= t("contacts.index.add_contact")
- contact = current_user.contact_for(@person)
- current_user.aspects.each do |aspect|
- if contact.try(:in_aspect?, aspect)
......
......@@ -13,9 +13,9 @@
-if AppConfig.chat.enabled?
= link_to aspect_toggle_chat_privilege_path(@aspect), id: "chat_privilege_toggle", class: "contacts_button", method: :put, remote: true do
-if @aspect.chat_enabled?
%i.entypo-chat.enabled.contacts-header-icon{title: t("aspects.edit.aspect_chat_is_enabled")}
%i.entypo-chat.enabled.contacts-header-icon{title: t("javascripts.contacts.aspect_chat_is_enabled")}
-else
%i.entypo-chat.contacts-header-icon{title: t("aspects.edit.aspect_chat_is_not_enabled")}
%i.entypo-chat.contacts-header-icon{title: t("javascripts.contacts.aspect_chat_is_not_enabled")}
= link_to @aspect, method: "delete", data: { confirm: t("aspects.edit.confirm_remove_aspect") }, class: "delete contacts_button", id: "delete_aspect" do
%i.entypo-trash.contacts-header-icon{title: t("delete")}
......
......@@ -107,6 +107,8 @@ module Diaspora
}
config.action_mailer.asset_host = AppConfig.pod_uri.to_s
config.action_view.raise_on_missing_translations = true
config.middleware.use Rack::OAuth2::Server::Resource::Bearer, "OpenID Connect" do |req|
Api::OpenidConnect::OAuthAccessToken
.valid(Time.zone.now.utc).find_by(token: req.access_token) || req.invalid_token!
......
......@@ -182,8 +182,6 @@ en:
rename: "Rename"
aspect_list_is_visible: "Contacts in this aspect are able to see each other."
aspect_list_is_not_visible: "Contacts in this aspect are not able to see each other."
aspect_chat_is_enabled: "Contacts in this aspect are able to chat with you."
aspect_chat_is_not_enabled: "Contacts in this aspect are not able to chat with you."
update: "Update"
updating: "Updating"
no_contacts_message:
......@@ -887,9 +885,24 @@ en:
openid:
name: "basic profile"
description: "This allows the application to read your basic profile"
extended:
sub:
name: "sub"
description: "This grants sub permissions to the application"
aud:
name: "aud"
description: "This grants aud permissions to the application"
name:
name: "name"
description: "This grants name permissions to the application"
nickname:
name: "nickname"
description: "This grants nickname permissions to the application"
profile:
name: "extended profile"
description: "This allows the application to read your extended profile"
picture:
name: "picture"
description: "This grants picture permissions to the application"
read:
name: "read profile, stream and conversations"
description: "This allows the application to read your stream, your conversations and your complete profile"
......
......@@ -92,6 +92,7 @@ en:
prefixFromNow: ""
suffixAgo: "ago"
suffixFromNow: "from now"
inPast: "any moment now"
seconds: "less than a minute"
minute: "about a minute"
minutes: "%d minutes"
......@@ -107,6 +108,8 @@ en:
contacts:
add_contact: "Add contact"
aspect_chat_is_enabled: "Contacts in this aspect are able to chat with you."
aspect_chat_is_not_enabled: "Contacts in this aspect are not able to chat with you."
aspect_list_is_visible: "Contacts in this aspect are able to see each other."
aspect_list_is_not_visible: "Contacts in this aspect are not able to see each other."
remove_contact: "Remove contact"
......
require "spec_helper"
describe LanguageHelper, type: :helper do
describe "#get_javascript_strings_for" do
it "generates a jasmine fixture", fixture: true do
save_fixture(get_javascript_strings_for("en", "javascripts").to_json, "locale_en_javascripts_json")
save_fixture(get_javascript_strings_for("en", "help").to_json, "locale_en_help_json")
end
end
end
describe("app.collections.Aspects", function(){
beforeEach(function(){
var locale = {
and: 'and',
comma: ',',
my_aspects: 'My Aspects'
};
var my_aspects = [
{ name: 'Work', selected: true },
{ name: 'Friends', selected: false },
{ name: 'Acquaintances', selected: false }
];
Diaspora.I18n.load(locale);
this.aspects = new app.collections.Aspects(my_aspects);
});
......@@ -52,7 +45,7 @@ describe("app.collections.Aspects", function(){
});
it("returns the name of the aspect", function(){
expect(this.aspects.toSentence()).toEqual('My Aspects');
expect(this.aspects.toSentence()).toEqual("My aspects");
});
});
......
describe("Handlebars helpers", function() {
beforeEach(function() {
Diaspora.I18n.load({people: {helper: {"is_not_sharing": "<%= name %> is not sharing with you"}}});
});
describe("sharingMessage", function() {
it("escapes the person's name", function() {
var person = { name: "\"><script>alert(0)</script> \"><script>alert(0)</script>"};
......
......@@ -3,19 +3,6 @@ describe("app.pages.AdminDashboard", function(){
spec.loadFixture("admin_dashboard");
this.view = new app.pages.AdminDashboard();
gon.podVersion = "0.5.1.2";
// disable jshint camelcase for i18n
/* jshint camelcase: false */
Diaspora.I18n.load({
admins: {
dashboard: {
up_to_date: "Your pod is up to date!",
outdated: "Your pod is outdated.",
compare_versions: "Latest d* release is <%= latestVersion%>, your pod is running <%= podVersion %>.",
error: "Unable to determine latest diaspora* version."
}
}
});
/* jshint camelcase: true */
});
describe("initialize" , function() {
......
......@@ -9,14 +9,6 @@ describe("app.pages.Contacts", function(){
collection: app.contacts
}
});
Diaspora.I18n.load({
contacts: {
aspect_list_is_visible: "Contacts in this aspect are able to see each other.",
aspect_list_is_not_visible: "Contacts in this aspect are not able to see each other.",
aspect_chat_is_enabled: "Contacts in this aspect are able to chat with you.",
aspect_chat_is_not_enabled: "Contacts in this aspect are not able to chat with you.",
}
});
});
context('toggle chat privilege', function() {
......
describe("app.views.AspectCreate", function() {
beforeEach(function() {
app.events.off("aspect:create");
// disable jshint camelcase for i18n
/* jshint camelcase: false */
Diaspora.I18n.load({
aspects: {
make_aspect_list_visible: "Make contacts in this aspect visible to each other?",
name: "Name",
create: {
add_a_new_aspect: "Add a new aspect",
success: "Your new aspect <%= name %> was created",
failure: "Aspect creation failed."
}
}
});
/* jshint camelcase: true */
});
context("without a person id", function() {
......
......@@ -10,14 +10,6 @@ describe("app.views.AspectMembership", function(){
app.flashMessages = new app.views.FlashMessages({ el: this.view.$("#flash-container") });
this.personId = $(".dropdown-menu").data("person_id");
this.personName = $(".dropdown-menu").data("person-short-name");
Diaspora.I18n.load({
aspect_dropdown: {
started_sharing_with: 'you started sharing with <%= name %>',
stopped_sharing_with: 'you stopped sharing with <%= name %>',
error: 'unable to add <%= name %>',
error_remove: 'unable to remove <%= name %>'
}
});
});
context('adding to aspects', function() {
......
describe("app.views.AspectsDropdown", function(){
beforeEach(function() {
spec.loadFixture("bookmarklet");
Diaspora.I18n.reset({
'aspect_dropdown': {
'select_aspects': "Select aspects",
'all_aspects': "All aspects",
'toggle': {
'zero': "Select aspects",
'one': "In <%= count %> aspect",
'other': "In <%= count %> aspects"
}}});
this.view = new app.views.AspectsDropdown({el: $('.aspect_dropdown')});
});
......
describe("app.views.AspectsList", function(){
beforeEach(function(){
setFixtures('<ul id="aspects_list"></ul>');
Diaspora.I18n.load({ aspect_navigation : {
'select_all' : 'Select all',
'deselect_all' : 'Deselect all'
}});
var aspects = [{ name: 'Work', selected: true },
{ name: 'Friends', selected: false },
{ name: 'Acquaintances', selected: false }];
......
......@@ -50,14 +50,12 @@ describe("app.views.CommentStream", function(){
});
it("doesn't add the comment to the view, when the request fails", function(){
// disable jshint camelcase for i18n
/* jshint camelcase: false */
Diaspora.I18n.load({failed_to_comment: "posting failed!"});
/* jshint camelcase: true */
this.request.respondWith({status: 500});
expect(this.view.$(".comment-content p").text()).not.toEqual("a new comment");
expect(this.view.$(".flash-message")).toBeErrorFlashMessage("posting failed!");
expect(this.view.$(".flash-message")).toBeErrorFlashMessage(
"Failed to comment. Maybe the author is ignoring you?"
);
});
});
......
......@@ -9,14 +9,6 @@ describe("app.views.Contact", function(){
aspect_memberships: [{id: 23, aspect: this.aspect1}]
});
this.view = new app.views.Contact({ model: this.model });
Diaspora.I18n.load({
contacts: {
add_contact: "Add contact",
remove_contact: "Remove contact",
error_add: "Couldn't add <%= name %> to the aspect :(",
error_remove: "Couldn't remove <%= name %> from the aspect :("
}
});
});
context("#presenter", function() {
......
......@@ -2,14 +2,6 @@ describe("app.views.Feedback", function(){
beforeEach(function(){
this.userAttrs = _.extend(factory.userAttrs(), {guid : -1});
loginAs(this.userAttrs);
Diaspora.I18n.load({stream : {
'like' : "Like",
'unlike' : "Unlike",
'public' : "Public",
'limited' : "Limted"
}});
var posts = $.parseJSON(spec.readFixture("stream_json"));
this.post = new app.models.Post(posts[0]);
......
......@@ -66,14 +66,14 @@ describe("app.views.Notifications", function(){
this.view.updateView(this.readN.data("guid"), this.readN.data("type"), true);
expect(this.readN.hasClass("unread")).toBeTruthy();
expect(this.readN.hasClass("read")).toBeFalsy();
expect(this.readN.find(".unread-toggle .entypo-eye").data("original-title")).toBe(
expect(this.readN.find(".unread-toggle .entypo-eye").attr("data-original-title")).toBe(
Diaspora.I18n.t("notifications.mark_read")
);
this.view.updateView(this.readN.data("guid"), this.readN.data("type"), false);
expect(this.readN.hasClass("read")).toBeTruthy();
expect(this.readN.hasClass("unread")).toBeFalsy();
expect(this.readN.find(".unread-toggle .entypo-eye").data("original-title")).toBe(
expect(this.readN.find(".unread-toggle .entypo-eye").attr("data-original-title")).toBe(
Diaspora.I18n.t("notifications.mark_unread")
);
});
......
describe("app.views.PodEntry", function() {
beforeEach(function() {
this.pod = new app.models.Pod({id : 123});
this.pod = factory.pod();
this.view = new app.views.PodEntry({
model: this.pod,
parent: document.createDocumentFragment()
......
......@@ -45,12 +45,6 @@ describe("app.views.Poll", function(){
describe("reshared post", function(){
beforeEach(function(){
Diaspora.I18n.load({
poll: {
go_to_original_post: "You can participate in this poll on the <%= original_post_link %>.",
original_post: "original post"
}
});
this.view.model.attributes.post_type = "Reshare";
this.view.model.attributes.root = {id: 1};
this.view.render();
......
......@@ -262,10 +262,6 @@ describe("app.views.Publisher", function() {
});
describe("_beforeUnload", function(){
beforeEach(function(){
Diaspora.I18n.load({ confirm_unload: "Please confirm that you want to leave this page - data you have entered won't be saved."});
});
it("calls _submittable", function(){
spyOn(this.view, "_submittable");
$(window).trigger('beforeunload');
......@@ -366,7 +362,6 @@ describe("app.views.Publisher", function() {
beforeEach( function(){
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
spec.loadFixture("status_message_new");
Diaspora.I18n.load({ stream: { public: 'Public' }});
this.viewAspectSelector = new app.views.PublisherAspectSelector({
el: $(".public_toggle .aspect_dropdown"),
......@@ -559,7 +554,6 @@ describe("app.views.Publisher", function() {
context('successful completion', function() {
beforeEach(function() {
Diaspora.I18n.load({ photo_uploader: { completed: '<%= file %> completed' }});
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');
this.uploader.onComplete(null, 'test.jpg', {
......@@ -597,7 +591,6 @@ describe("app.views.Publisher", function() {
context('unsuccessful completion', function() {
beforeEach(function() {
Diaspora.I18n.load({ photo_uploader: { completed: '<%= file %> completed' }});
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');
this.uploader.onComplete(null, 'test.jpg', {
......
......@@ -53,18 +53,6 @@ describe("app.views.StreamPost", function(){
beforeEach(function(){
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
Diaspora.I18n.load({stream : {
reshares : {
one : "<%= count %> reshare",
other : "<%= count %> reshares"
},
likes : {
zero : "<%= count %> Likes",
one : "<%= count %> Like",
other : "<%= count %> Likes"
}
}});
});
context("reshares", function(){
......
......@@ -24,6 +24,10 @@ describe("Diaspora.I18n", function() {
Diaspora.I18n.reset(); // run tests with clean locale
});
afterEach(function() {
Diaspora.I18n.load(spec.defaultLocale, "en"); // leave the tests with the default locale
});
describe("::load", function() {
it("sets the class's locale variable", function() {
Diaspora.I18n.load(locale, "en", locale);
......@@ -65,8 +69,10 @@ describe("Diaspora.I18n", function() {
expect(translation).toEqual("it works!");
});
it("returns an empty string if the translation is not found", function() {
expect(Diaspora.I18n.t("missing.locale")).toEqual("");
it("throws an error if the translation is not found", function() {
expect(function() {
return Diaspora.I18n.t("missing.locale");
}).toThrowError("Missing translation: missing.locale");
});
it("falls back on missing key", function() {
......
......@@ -37,7 +37,6 @@ var customMatchers = {
}
};
beforeEach(function() {
jasmine.clock().install();
jasmine.Ajax.install();
......@@ -52,8 +51,6 @@ beforeEach(function() {
var Page = Diaspora.Pages["TestPage"];
$.extend(Page.prototype, Diaspora.EventBroker.extend(Diaspora.BaseWidget));
Diaspora.I18n.load({}, "en", {});
Diaspora.page = new Page();
Diaspora.page.publish("page/ready", [$(document.body)]);
......@@ -74,8 +71,6 @@ beforeEach(function() {
});
afterEach(function() {
//spec.clearLiveEventBindings();
jasmine.clock().uninstall();
jasmine.Ajax.uninstall();
......@@ -107,42 +102,6 @@ window.logout = function logout(){
return app.currentUser;
};
window.hipsterIpsumFourParagraphs = "Mcsweeney's mumblecore irony fugiat, ex iphone brunch helvetica eiusmod retro" +
" sustainable mlkshk. Pop-up gentrify velit readymade ad exercitation 3 wolf moon. Vinyl aute laboris artisan irony, " +
"farm-to-table beard. Messenger bag trust fund pork belly commodo tempor street art, nihil excepteur PBR lomo laboris." +
" Cosby sweater american apparel occupy, locavore odio put a bird on it fixie kale chips. Pariatur semiotics flexitarian " +
"veniam, irure freegan irony tempor. Consectetur sriracha pour-over vice, umami exercitation farm-to-table master " +
"cleanse art party." + "\n" +
"Quinoa nostrud street art helvetica et single-origin coffee, stumptown bushwick selvage skateboard enim godard " +
"before they sold out tumblr. Portland aesthetic freegan pork belly, truffaut occupy assumenda banksy 3 wolf moon " +
"irure forage terry richardson nulla. Anim nostrud selvage sartorial organic. Consequat pariatur aute fugiat qui, " +
"organic marfa sunt gluten-free mcsweeney's elit hella whatever wayfarers. Leggings pariatur chambray, ullamco " +
"flexitarian esse sed iphone pinterest messenger bag Austin cred DIY. Duis enim squid mcsweeney's, nisi lo-fi " +
"sapiente. Small batch vegan thundercats locavore williamsburg, non aesthetic trust fund put a bird on it gluten-free " +
"consectetur." + "\n" +
"Viral reprehenderit iphone sapiente exercitation. Enim nostrud letterpress, tempor typewriter dreamcatcher tattooed." +
" Ex godard pariatur voluptate est, polaroid hoodie ea nulla umami pickled tempor portland. Nostrud food truck" +
"single-origin coffee skateboard. Fap enim tumblr retro, nihil twee trust fund pinterest non jean shorts veniam " +
"fingerstache small batch. Cred whatever photo booth sed, et dolore gastropub duis freegan. Authentic quis butcher, " +
"fanny pack art party cupidatat readymade semiotics kogi consequat polaroid shoreditch ad four loko." + "\n" +
"PBR gluten-free ullamco exercitation narwhal in godard occaecat bespoke street art veniam aesthetic jean shorts " +
"mlkshk assumenda. Typewriter terry richardson pork belly, cupidatat tempor craft beer tofu sunt qui gentrify eiusmod " +
"id. Letterpress pitchfork wayfarers, eu sunt lomo helvetica pickled dreamcatcher bicycle rights. Aliqua banksy " +
"cliche, sapiente anim chambray williamsburg vinyl cardigan. Pork belly mcsweeney's anim aliqua. DIY vice portland " +
"thundercats est vegan etsy, gastropub helvetica aliqua. Artisan jean shorts american apparel duis esse trust fund.";
spec.clearLiveEventBindings = function() {
var events = jQuery.data(document, "events");
for (var prop in events) {
if(events.hasOwnProperty(prop)) {
delete events[prop];
}
}
};
spec.content = function() {
return $("#jasmine_content");
};
......@@ -195,6 +154,8 @@ spec.retrieveFixture = function(fixtureName) {
return xhr.responseText;
};
spec.loadFixtureCount = 0;
spec.cachedFixtures = {};
spec.defaultLocale = JSON.parse(spec.readFixture("locale_en_javascripts_json"));
Diaspora.I18n.reset(spec.defaultLocale);
......@@ -211,6 +211,24 @@ var factory = {
window.gon = { preloads: {} };
_.extend(window.gon.preloads, defaults, overrides);
},
pod: function(overrides) {
var defaultAttrs = {
"id": 4,
"host": "pod.example.org",
"port": null,
"ssl": true,
"status": "no_errors",
"checked_at": "2020-01-01T13:37:00.000Z",
"response_time": 100,
"offline": false,
"offline_since": null,
"created_at": "2010-01-01T13:37:00.000Z",
"software": "diaspora 1.2.3.0",
"error": "ConnectionTester::Failure: #<Faraday::TimeoutError>"
};
return new app.models.Pod(_.extend(defaultAttrs, overrides));
}
};
......
......@@ -44,3 +44,6 @@ RSpec::Rails::ControllerExampleGroup.class_eval do
include JasmineFixtureGeneration
end
RSpec::Rails::HelperExampleGroup.class_eval do
include JasmineFixtureGeneration
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment